﻿using System;
using System.ComponentModel;
using System.Net;
using System.Reflection;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using SLPropertyGrid.System.ComponentModel;
using SLPropertyGrid.TypeConverters;

namespace SLPropertyGrid
{
    public class Property : INotifyPropertyChanged
    {
        private object _instance;
        private PropertyInfo _propertyInfo;
        private string _displayname;
        private string _category;
        private bool _readOnly = false;
        private string _description;

        public string DisplayName
        {
            get { return _displayname; }
        }

        public string Name
        {
            get { return PropertyInfo.Name; }
        }

        public object Value
        {
            get { return _value; }
            set
            {
                object originalValue = _value;
                _value = value;
                try
                {
                    Type propertyType = this.PropertyInfo.PropertyType;
                    if (((propertyType == typeof(object)) || ((value == null) && propertyType.IsClass)) || ((value != null) && propertyType.IsAssignableFrom(value.GetType())))
                    {

                        PropertyInfo.SetValue(_instance, value, (BindingFlags.NonPublic | BindingFlags.Public), null, null, null);

                    }
                    else
                    {

                        try
                        {
                            TypeConverter tc = TypeConverterHelper.GetConverter(propertyType);
                            if (tc != null)
                            {
                                object convertedValue = tc.ConvertFrom(value);
                                PropertyInfo.SetValue(_instance, convertedValue, null);
                            }
                        }
                        catch (Exception e)
                        {
                            _value = originalValue;
                            NotifyPropertyChanged("Value");
                        }
                    }
                }
                catch (MethodAccessException ex)
                {
                    _value = originalValue;
                    _readOnly = true;
                    NotifyPropertyChanged("Value");
                    NotifyPropertyChanged("CanWrite");

                }
            }
        }

        private object _value;

        public Type PropertyType
        {
            get { return PropertyInfo.PropertyType; }
        }

        public string Category
        {
            get
            {
                if (this._category == null)
                {
                    object[] attrs = PropertyInfo.GetCustomAttributes(typeof(CategoryAttribute), true);
                    if (attrs.Length > 0)
                        this._category = ((CategoryAttribute)attrs[0]).Category;
                }
                return this._category;
            }
        }

        public bool CanWrite
        {
            get
            {
                return PropertyInfo.CanWrite && !_readOnly;
            }
        }

        public string Description
        {
            get { return _description; }
        }

        internal PropertyInfo PropertyInfo
        {
            get { return _propertyInfo; }
        }

        public Property(object instance, PropertyInfo property)
        {

            _instance = instance;
            _propertyInfo = property;
            _value = property.GetValue(instance, null);
            _displayname = GetPropertyName(property);
            _description = GetPropertyDescription(property);
        }

        public Control GetEditor(out DependencyProperty propertyToBind)
        {
            Control editor=null;
            propertyToBind = null;
            var editorAttributes = _propertyInfo.GetCustomAttributes(typeof(EditorAttribute), true);
            foreach (var editorAttribute in editorAttributes)
            {
                EditorAttribute attribute = editorAttribute as EditorAttribute;
 
                if (attribute != null)
                {
                    Type type = Type.GetType(attribute.EditorTypeName);

                    if (type != null) editor = Activator.CreateInstance(type, null) as Control;
                    if(editor!=null)
                    {
                        var field = editor.GetType().GetField(attribute.PropertyToBind,BindingFlags.Static|BindingFlags.FlattenHierarchy|BindingFlags.Public);
                        if(field!=null)    
                          propertyToBind =  field.GetValue(editor) as DependencyProperty;
                        break;
                    }
                }
            }
            

            return editor;
        }



 



        private static string GetPropertyName(PropertyInfo propertyInfo)
        {
            var displayNameAttributes = propertyInfo.GetCustomAttributes(typeof(DisplayNameAttribute), true);
            return (displayNameAttributes.Length > 0) ? ((DisplayNameAttribute)(displayNameAttributes[0])).DisplayName : propertyInfo.Name;
        }

        private static string GetPropertyDescription(PropertyInfo propertyInfo)
        {
            var displayNameAttributes = propertyInfo.GetCustomAttributes(typeof(DescriptionAttribute), true);
            return (displayNameAttributes.Length > 0) ? ((DescriptionAttribute)(displayNameAttributes[0])).Description : null;
        }

        private void NotifyPropertyChanged(String info)
        {
            if (PropertyChanged != null)
            {
                PropertyChanged(this, new PropertyChangedEventArgs(info));
            }
        }

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        #endregion
    }

    namespace System.ComponentModel
    {
        public class DisplayNameAttribute : Attribute
        {
            public string DisplayName { get; private set; }
            public DisplayNameAttribute(string displayName)
            {
                DisplayName = displayName;
            }
        }
    }

}
